/*
 Автор: Швейкин Евгений Юрьевич

 Функциональное описание:     


    1 Пераметры 
    1.1 Размер очереди SEQ_SZ
    Определяет сколько пакетов может быть накоплено в памяти. 
    Размер используемой памяти для хранения данных рассчитвается как SEQ_SZ * 2048 байт.
    Размер фифо для хранения индексов равен SEQ_SZ.
    
    1.2 Смещение до первого блока MEM_OFST
    Задает адрес первого блока памяти, используемой для записи принятых данных. Задается в байтах.
    
    1.1 CSR (регистры управления и статуса)
    Адрес   Имя                 Доступ      Описание битов
    0       status              R           [0] - Флаг пустого буфера
                                            [1] - Флаг переполнения буфера    
                                            [31:2] - Резерв
                                            
    1       ctrl                W           [3:0] - Команда
                                            [31:4] - Резерв

    2       ctrl                R           [9:0]  - размер принятого пакета
                                            [31:10] - Резерв
                                            
    3       ctrl                R           [31:0] - Адрес блока данных
*/
//--------------------------------------------------------------------------------------------------

// ********************** МАКРОСЫ ******************************************************************
`include "eth_rx_def.vh"
/*
    // Описывает процесс модуля выделения фронтов сигнала. Строб формируется на том же такте.
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define ETH_RX_FRONT(clk, name)                            \
        logic name``_0;                                 \
        logic name``_rise;                              \
        logic name``_fall;                              \
        logic name``_edge;                              \
        assign name``_rise = name & (~name``_0);        \
        assign name``_fall = name``_0 & (~name);        \
        assign name``_edge = name ^ name``_0;           \
        always_ff @(posedge clk)                        \
        begin                                           \
            name``_0 <= name;                           \
        end

    // Описывает процесс пересинхронизации для исключения метастабильности
    `define ETH_RX_RESYNC(clk, name, bw)                       \
        logic [bw - 1:0] name``_resync_``clk``_0;       \
        logic [bw - 1:0] name``_``clk;                  \
        always_ff @(posedge clk)                        \
        begin                                           \
            name``_resync_``clk``_0 <= name;            \
            name``_``clk    <= name``_resync_``clk``_0; \
        end
*/
// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module eth_rx
#(
    parameter SEQ_SZ        = 256,     // Размер очереди
    parameter MEM_OFST      = 0       // Смещение до первого блока
)
(

    input  logic            reset_n,
    input  logic            clk,
    
    // Сигналы входного потока
    input  logic [3:0]      coe_rx_data,
    input  logic            coe_rx_dv,
    input  logic            coe_rx_clk,
    
    input  logic            coe_rx_col,
    input  logic            coe_rx_crs,
    input  logic            coe_rx_er,
    
    // Управление модулем
    input  logic [5:0]      avs_csr_address,
    input  logic [5:0]      avs_csr_burstcount,
    input  logic            avs_csr_beginbursttransfer,
    input  logic            avs_csr_write,
    input  logic [31:0]     avs_csr_writedata,

    input  logic            avs_csr_read,
    output logic [31:0]     avs_csr_readdata,
    output logic            avs_csr_readdatavalid,
    
    output logic            avs_csr_waitrequest,

    // Сигналы шины авалон
    input  logic            avm_wr_waitrequest,
    
    output logic [31:0]     avm_wr_address,
    output logic            avm_wr_write,
    output logic [9:0]      avm_wr_burstcount,
    output logic            avm_wr_beginbursttransfer,
    output logic [7:0]      avm_wr_writedata

);
    
localparam SEQ_BW       = $clog2(SEQ_SZ);   // Ширина шины, адресующей элементы очереди

//  ********************* КОНСТАНТЫ ****************************************************************


//  ********************* ТИПЫ ДАННЫХ **************************************************************
    
    typedef struct packed 
    {   
        logic [27:0]                    res;
        logic [3:0]                     cmd;        // команда
    } ctrl_t;
    
    // Статус
    typedef struct packed 
    {   
        logic [29:0]                    res;
        logic                           full;       // Флаг переполнения буфера
        logic                           empty;      // Флаг пустого буфера
    } status_t;
    
    typedef struct packed 
    {   logic [3:0]                     data;
        logic                           wrreq;
        logic [11:0]                    wrusedw;
        logic                           wrfull;
        
        logic [7:0]                     q;
        logic                           rdreq;
        logic [10:0]                    rdusedw;
        logic                           rdempty;
        logic                           dval;
    } data_fifo_t;
    
    typedef struct packed 
    {   logic [31:0]                    data;
        logic                           wrreq;
        logic [SEQ_BW:0]                wrusedw;
        logic                           wrfull;
        
        logic [31:0]                    q;
        logic                           rdreq;
        logic [SEQ_BW:0]                rdusedw;
        logic                           rdempty;
    } index_fifo_t;
    
//  ********************* ПЕРЕМЕННЫЕ ***************************************************************
    data_fifo_t                     data_fifo;
    index_fifo_t                    index_fifo;
    status_t                        status;
    ctrl_t                          ctrl;
    
    // Для модуля avs
    logic [3:0]                     csr_address;
    logic                           rx_val;
    logic [31:0]                    rx_data; 
    logic                           tx_req;
    logic                           tx_val;
    logic [31:0]                    tx_data; 
    
    //
    logic [31:0]                    base_addr; 
    logic                           soft_reset;
    logic [12:0]                    frame_sz_cnt; 
    logic                           start;          // Запуск записи
    logic                           stop;           // Остановка записи    
    logic                           busy;           // Модуль записи в память занят
    logic                           data_req;       // Запрос байта из фифо
    logic [SEQ_BW - 1:0]  block_cnt;
    
// ********************* INITIAL *******************************************************************

//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ASSIGN *******************************************************************
    // Работаем пока фифо индексов не переполнилось
    assign status.res       = 30'd0;
    assign status.empty     = index_fifo.rdempty;
    assign status.full      = index_fifo.wrfull;
    assign data_fifo.rdreq  = (~data_fifo.rdempty) & data_req; 
    
// ********************* ПРОЦЕССЫ ******************************************************************
    `ETH_RX_FRONT (coe_rx_clk, coe_rx_dv)
    `ETH_RX_FRONT (coe_rx_clk, busy)
    `ETH_RX_FRONT (clk, coe_rx_dv_clk)
    `ETH_RX_FRONT (coe_rx_clk, coe_rx_dv_0)
    
    `ETH_RX_RESYNC(clk, coe_rx_dv, 1)
    
    // Счетчик размера пакета
    always_ff @(posedge coe_rx_clk)
    begin
        if (coe_rx_dv_rise)
            frame_sz_cnt    <= 13'd0;
        else if (coe_rx_dv)
            frame_sz_cnt    <= frame_sz_cnt + 13'd1;
        else;
    end    
    
    // Формируем сигналы записи в память
    always_ff @(posedge coe_rx_clk)
    begin
        // Все входные данные кидаем в фифо
        data_fifo.wrreq  <= coe_rx_dv;
        data_fifo.data   <= coe_rx_data;
    end
    
    // Запись размера пакета в фифо индексов
    always_ff @(posedge coe_rx_clk)
    begin
        // index_fifo.wrreq  <= coe_rx_dv_fall & (~index_fifo.wrfull);
        index_fifo.wrreq  <= 1'b0;
        if (coe_rx_dv_fall && (~index_fifo.wrfull)) begin
            index_fifo.wrreq  <= 1'b1;
            index_fifo.data     <= {
                {16 - SEQ_BW{1'b0}},
                block_cnt,
                3'd0,
                frame_sz_cnt >> 1
                };
        end
        else;
    end
   

    // Сформируем сигнал начала записи из фифо в память
    always_ff @(posedge coe_rx_clk, posedge soft_reset)
    begin
        if (soft_reset) begin
            // При сбросе переходим на 0 блок
            block_cnt   <= {SEQ_BW{1'b0}};
        end
        else begin
            data_fifo.dval  <= data_fifo.rdreq;
            start           <= 1'b0;
            stop            <= coe_rx_dv_0_fall;
            // По переднему фронту запустим запись в память, по адресу соответствующему блоку
            if (coe_rx_dv_rise && (!index_fifo.wrfull)) begin
                start       <= 1'b1;
                base_addr   <= MEM_OFST + (block_cnt << 11);
            end 
            // По заднему фронту увеличиваем номер блока
            else if (coe_rx_dv_fall) begin
                block_cnt   <= block_cnt + 1'd1;
            end
            else;
        end
    end   

// ************* ДАЛЬШЕ ИДУТ ПРОЦЕССЫ НА ЧАСТОТЕ ШИНЫ AVALON ***************************************
    // Мультиплексор команд
    always_ff @(posedge clk)
    begin
        // Ресет опускаем только когда нет пакетов на входе
        if (!coe_rx_dv_clk)
            soft_reset          <= coe_rx_dv_clk;
        else;
        index_fifo.rdreq    <= 1'b0;
        case (ctrl.cmd)
            1: soft_reset       <= 1'b1;
            2: index_fifo.rdreq <= 1'b1;
            default: ;
        endcase
    end
    
    always_ff @(posedge clk)
    begin
        ctrl.cmd <= 4'd0;
        if (rx_val) begin
            // Мультиплексор приемника данных по avs
            case (csr_address)
                0:  ;
                1:  ctrl     <= rx_data;      // Команда управления
                default ;
            endcase;
        end
        else;
    end

    always_ff @(posedge clk)
    begin
        tx_val  <= tx_req;
        // Мультиплексор передатчика данных по avs
        if (tx_req) begin
            case (csr_address)
                0:  tx_data <= status;                  // Статус
                1:  tx_data <= ctrl;                    // Команда управления
                2:  tx_data <= index_fifo.q[9:0];       // Размер пакета в словах
                3:  tx_data <= index_fifo.q[15 + SEQ_BW:16] << 11;    // Адрес блока
                default ;
            endcase;
        end
        else;
    end

// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************

    // Упрощает шину авалон до сигналов адреса, данных и подтверждений
    ctrl_avs
    ctrl_avs_inst1
    (
        .reset_n                (reset_n),
        .clk                    (clk),

        .avs_address            (avs_csr_address),
        .avs_burstcount         (avs_csr_burstcount),
        .avs_beginbursttransfer (avs_csr_beginbursttransfer),
        .avs_write              (avs_csr_write),
        .avs_writedata          (avs_csr_writedata),

        .avs_read               (avs_csr_read),
        .avs_readdata           (avs_csr_readdata),
        .avs_readdatavalid      (avs_csr_readdatavalid),
                
        .avs_waitrequest        (avs_csr_waitrequest),
               
        .rx_val                 (rx_val),
        .rx_data                (rx_data),
                
        .tx_req                 (tx_req),
        .tx_val                 (tx_val),
        .tx_data                (tx_data),

        .addr                   (csr_address)
    );

    // Буфер FIFO для хранения входных данных
    dcfifo_mixed_widths
    #(  .add_usedw_msb_bit      ("ON"),             // разрядность usedw на весь диапазон
        //.intended_device_family ("ARRIA II GX"),  // Arria II GX
        .lpm_showahead          ("OFF"),            // режим ASK выключен
        .lpm_type               ("dcfifo_mixed_widths"),         // Разные частоты входа/выхода
        .overflow_checking      ("ON"),             // Проверка переполнения
        .underflow_checking     ("ON"),             // Проверка опустошения
        .use_eab                ("ON"), 
        .rdsync_delaypipe       (4),                
        .wrsync_delaypipe       (4),
        
        .lpm_numwords           (4096),             // Объем фифо на 1 пакет? (слов 4 бита)
        
        .lpm_width              (4),                // Разрядность входных данных 
        .lpm_widthu             (12),               // Разрядность адреса входа
        .lpm_width_r            (8),                // Разрядность выходных данных
        .lpm_widthu_r           (11)                // Разрядность адреса выхода
    )
    data_fifo_inst1
    (   .aclr                   (~reset_n | coe_rx_dv_rise),
    
        // Запись в фифо
        .wrclk                  (coe_rx_clk),
        .wrreq                  (data_fifo.wrreq),
        .data                   (data_fifo.data),
        .wrfull                 (data_fifo.wrfull),
        .wrusedw                (data_fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (coe_rx_clk),
        .rdreq                  (data_fifo.rdreq),
        .q                      (data_fifo.q),
        .rdusedw                (data_fifo.rdusedw),
        .rdempty                (data_fifo.rdempty)
    );

    // Буфер FIFO для хранения индексов записанных пакетов
    dcfifo_mixed_widths
    #(  
        .add_usedw_msb_bit      ("ON"),             // разрядность usedw на весь диапазон
        .lpm_showahead          ("ON"),             // режим ASK включен
        .lpm_type               ("dcfifo_mixed_widths"),         // Разные частоты входа/выхода
        .overflow_checking      ("ON"),             // Проверка переполнения
        .underflow_checking     ("ON"),             // Проверка опустошения
        .use_eab                ("ON"), 
        .rdsync_delaypipe       (4),                
        .wrsync_delaypipe       (4),
        
        .lpm_numwords           (SEQ_SZ),         // Объем фифо
        
        .lpm_width              (32),               // Разрядность данных 
        .lpm_widthu             (SEQ_BW), // Разрядность адреса входа
        .lpm_width_r            (32),
        .lpm_widthu_r           (SEQ_BW)  // Разрядность адреса выхода
    )
    index_fifo_inst1
    (   .aclr                   (~reset_n | soft_reset),
   
        // Запись в фифо
        .wrclk                  (coe_rx_clk),         
        .wrreq                  (index_fifo.wrreq),                        
        .data                   (index_fifo.data),
        .wrfull                 (index_fifo.wrfull),
        .wrusedw                (index_fifo.wrusedw),
      
        // Чтение из фифо
        .rdclk                  (clk),
        .rdreq                  (index_fifo.rdreq),
        .q                      (index_fifo.q),
        .rdusedw                (index_fifo.rdusedw),
        .rdempty                (index_fifo.rdempty)
    );
    
    // Запись из фифо в память
    fifo_to_amm_burst_bw
    #(
        .FIFO_SZ                (2048),
        .BURST_SZ               (16),
        .BW                     (8)
    )
    fifo_to_amm_burst_bw_inst1
    (   
        .reset_n                (reset_n & (~soft_reset)),
        
        .clk_ctrl               (coe_rx_clk),
        .start_i                (start),
        .stop_i                 (stop),
        .data_size_i            (32'd2048),
        .base_addr_i            (base_addr),
        .busy_o                 (busy),
        
        .clk_fifo               (coe_rx_clk),
        .fifo_wre_i             (data_fifo.dval),
        .fifo_data_i            (data_fifo.q),
        .fifo_wrfull_o          (),
        
        .data_req_o             (data_req),
        
        // Сигналы шины авалон
        .clk_amm                (clk),
        
        .avm_waitrequest        (avm_wr_waitrequest),
        .avm_address            (avm_wr_address),
        .avm_write              (avm_wr_write),
        .avm_burstcount         (avm_wr_burstcount),
        .avm_beginbursttransfer (avm_wr_beginbursttransfer),
        .avm_writedata          (avm_wr_writedata)
    );
    
endmodule 